package it.polimi.jita.cp.block;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.jfree.chart.ChartFrame;
import org.jfree.chart.JFreeChart;

public abstract class Block<I extends Input, O extends Output> {
	private I input;
	private O output;

	@SuppressWarnings("unchecked")
	public void setInput(Input input) {
		this.input = (I) input;
		output = null;
	}

	public O getOutput() {
		if (output == null)
			output = computeOutput();

		return output;
	}

	protected abstract O computeOutput();

	protected abstract Set<JFreeChart> createCharts();

	protected abstract Map<List<CSVRecord>, CSVRecord> createCSVFiles();

	private Output checkCharts() {
		Output o;
		try {
			o = getOutput();

			if (!o.isWithCharts()) {
				Set<JFreeChart> set = createCharts();
				for (JFreeChart jFreeChart : set) {
					o.addJFreeChart(jFreeChart);
				}
			}
			return o;
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return null;
	}

	private Output checkCsvFiles() {
		Output o;
		try {
			o = getOutput();

			if (!o.isWithCsvFiles()) {
				Map<List<CSVRecord>, CSVRecord> map = createCSVFiles();
				for (Entry<List<CSVRecord>, CSVRecord> e : map.entrySet()) {
					o.addCsvFile(e.getKey(), e.getValue());
				}

			}
			return o;
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return null;
	}

	public void printCSVFiles() throws IOException {
		List<String> filePaths = getInput().getCsvFilePaths();
		Output o = checkCsvFiles();
		Set<List<CSVRecord>> csvFiles = input.isWithHeaders() ? o
				.getCSVRecordsWithHeader() : o.getCSVRecords();

		if (filePaths.size() > csvFiles.size())
			throw new IllegalArgumentException(
					String.format(
							"Number of file paths (%s) must be less or equal to number of csv files (%s)",
							filePaths.size(), csvFiles.size()));
		int i = 0;
		for (List<CSVRecord> list : csvFiles) {

			String fp = filePaths.get(i++);
			Writer writer = new PrintWriter(fp, "UTF-8");
			CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT);
			for (CSVRecord csvRecord : list) {
				printer.printRecord(csvRecord);
			}
			printer.close();
			System.out.println(String.format("CSV file '%s' created.", fp));
		}

	}

	public void showCharts() {
		Output o = checkCharts();
		Set<JFreeChart> charts = o.getCharts();
		for (JFreeChart jFreeChart : charts) {
			ChartFrame frame = new ChartFrame(jFreeChart.getTitle().getText(),
					jFreeChart);
			frame.pack();
			frame.setVisible(true);
			frame.setDefaultCloseOperation(ChartFrame.DISPOSE_ON_CLOSE);
		}
	}

	protected I getInput() {
		return input;
	}

	public void saveCharts() {
		// TODO Auto-generated method stub

	}

	public static CSVRecord createCSVRecord(boolean isString, Object... values) {
		if (values.length < 1)
			throw new IllegalArgumentException(
					"For creating a CVSRecord is need to pass at least one value!");
		StringBuilder sb = new StringBuilder();

		if (isString) {
			for (Object value : values) {
				if (sb.length() > 0) {
					sb.append(String.format(",\"%s\"", value));
				} else {
					sb.append(String.format("\"%s\"", value));
				}
			}
		} else {
			for (Object value : values) {
				if (sb.length() > 0) {
					sb.append(String.format(",%s", value));
				} else {
					sb.append(String.format("%s", value));
				}
			}
		}

		try {
			return CSVParser.parse(sb.toString(), CSVFormat.DEFAULT)
					.getRecords().get(0);
		} catch (IOException e) {
			System.err.println(String.format(
					"Problem occurs during parsing CSV string '%s'.",
					sb.toString()));
			return null;
		}
	}
}
